package com.imooc.netty.ch3;

import com.imooc.netty.ch6.AuthHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.AttributeKey;

/**
 * @author
 */
public final class Server {

    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1); //接入新连接的线程
        EventLoopGroup workerGroup = new NioEventLoopGroup();         //处理连接数据的线程

        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    //指定服务端的Channel类
                    .channel(NioServerSocketChannel.class)
                    //attr()方法可以给服务端的channel，也就是NioServerSocketChannel指定一些自定义属性，
                    // 然后我们可以通过channel.attr()取出这个属性，
                    // 比如，上面的代码我们指定我们服务端channel的一个serverName属性，
                    // 属性值为nettyServer，其实说白了就是给NioServerSocketChannel维护一个map而已，
                    // 通常情况下，我们也用不上这个方法。

                    .attr(AttributeKey.newInstance("serverName"), "nettyServer")
                    //childOption()可以给每条连接设置一些TCP底层相关的属性，比如上面，我们设置了三种TCP属性，其中
                    //
                    //
                    //ChannelOption.SO_KEEPALIVE表示是否开启TCP底层心跳机制，true为开启
                    //
                    //ChannelOption.TCP_NODELAY表示是否开始Nagle算法，true表示关闭，false表示开启，通俗地说，如果要求高实时性，有数据发送时就马上发送，就关闭，如果需要减少发送次数减少网络交互，就开启。
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    //除了给每个连接设置这一系列属性之外，我们还可以给服务端channel设置一些属性，最常见的就是so_backlog，如下设置
                    //serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024)
                    //
                    //表示系统用于临时存放已完成三次握手的请求的队列的最大长度，如果连接建立频繁，服务器处理创建新连接较慢，可以适当调大这个参数
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    //childAttr可以给每一条连接指定自定义属性，然后后续我们可以通过channel.attr()取出该属性
                    .childAttr(AttributeKey.newInstance("childAttr"), "childAttrValue")
                    //用于指定在服务端启动过程中的一些逻辑，通常情况下呢，我们用不着这个方法。
                    .handler(new ChannelInitializer<ServerSocketChannel>() {

                                 @Override
                                 protected void initChannel(ServerSocketChannel ch) throws Exception {
                                     ch.pipeline().addLast(new ServerHandler());
                                     //ch.pipeline().addLast(new ServerHandler2());
                                 }
                             }
                    )
                    //用于指定处理新连接数据的读写处理逻辑
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) {
                            ch.pipeline().addLast(new AuthHandler());

                        }
                    });

            ChannelFuture f = b.bind(8888).sync();

            f.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}